﻿using System;
using System.Collections.Generic;
using System.Threading;
using AccelerateSensor.Console.Common;
using AccelerateSensor.Console.Tools;
using Newtonsoft.Json.Linq;
using System.Linq;

namespace AccelerateSensor.Console
{
    public class SocketMsgHandler
    {
        private readonly Dictionary<int, Action<JObject>> _cmdActionDic;
        private readonly ISocketMsgHandlerObserver _observer;

        private readonly Queue<string> _msgQueue = new Queue<string>();
        private readonly Semaphore _msgSemaphore = new Semaphore(0, int.MaxValue);

        #region 线程
        private Thread _threadWorker;
        private bool _threadWorking;
        private readonly object _threadWorkingLockHelper = new object();

        private bool IsThreadWorking
        {
            get
            {
                bool ret;
                lock (_threadWorkingLockHelper)
                {
                    ret = _threadWorking;
                }

                return ret;
            }
            set
            {
                lock (_threadWorkingLockHelper)
                {
                    _threadWorking = value;
                }
            }
        } 
        #endregion

        public SocketMsgHandler(ISocketMsgHandlerObserver observer)
        {
            _observer = observer;
            _cmdActionDic = new Dictionary<int, Action<JObject>>()
            {
                {Constants.CmdHeartbeat, OnHeartbeat},
                {Constants.CmdGatewayAttribute, OnGatewayAttribute},
                {Constants.CmdNodeAttribute, OnNodeAttribute},
                {Constants.CmdNodeData, OnNodeData}
            };
        }

        public void Start()
        {
            IsThreadWorking = true;
            _threadWorker = new Thread(DoWork)
            {
                IsBackground = true
            };
            _threadWorker.Start();
        }

        public void Stop()
        {
            IsThreadWorking = false;
            if (_threadWorker != null)
            {
                _threadWorker.Join();
                _threadWorker = null;
            }

            ClearMessage();
        }

        #region 消息队列操作

        public void AddMessage(string msg)
        {
            lock (_msgQueue)
            {
                _msgQueue.Enqueue(msg);
                _msgSemaphore.Release();
            }
        }

        private bool HasMessage()
        {
            bool ret;
            lock (_msgQueue)
            {
                ret = _msgQueue.Any();
            }

            return ret;
        }

        private string PickMessage()
        {
            string msg = null;
            lock (_msgQueue)
            {
                if (_msgQueue.Count > 0)
                {
                    msg = _msgQueue.Peek();
                    _msgQueue.Dequeue();
                }
            }

            return msg;
        }

        private void ClearMessage()
        {
            lock (_msgQueue)
            {
                _msgQueue.Clear();
            }
        }

        #endregion

        /// <summary>
        /// 日志文件写入线程执行的方法
        /// </summary>
        private void DoWork()
        {
            while (IsThreadWorking)
            {
                if (!_msgSemaphore.WaitOne(1))
                {
                    continue;
                }

                HandleMessage(PickMessage());
            }
        }

        //处理消息
        private void HandleMessage(string message)
        {
            try
            {
                var msgObjects = JObject.Parse(message);

                JToken cmdToken;
                msgObjects.TryGetValue("cmd", out cmdToken);
                var cmd = cmdToken?.ToObject<int>() ?? int.MinValue;
                System.Console.WriteLine($"cmd = {cmd}");

                if (_cmdActionDic.ContainsKey(cmd))
                {
                    _cmdActionDic[cmd]?.Invoke(msgObjects);
                }
            }
            catch (Exception e)
            {
                var msg = $"HandleMessage exception: {e.Message}";
                System.Console.WriteLine(msg);
                LogHelper.AddErrorLog(msg);
            }
        }

        //心跳
        private void OnHeartbeat(JObject msgObjects)
        {
            System.Console.WriteLine("======================= Receive heartbeat ========================");
            _observer.ReceiveHeartbeat();
        }

        #region 网关属性信息

        //网关属性信息
        private void OnGatewayAttribute(JObject msgObjects)
        {
            JToken gatewayUuidToken;
            msgObjects.TryGetValue("gatewayUUID", out gatewayUuidToken);
            var gatewayUuid = gatewayUuidToken?.ToObject<string>();

            JToken gatewayTypeToken;
            msgObjects.TryGetValue("gatewayType", out gatewayTypeToken);
            var gatewayType = gatewayTypeToken?.ToObject<int>() ?? int.MinValue;

            JToken dataTypeToken;
            msgObjects.TryGetValue("dataType", out dataTypeToken);
            var dataType = dataTypeToken?.ToObject<int>() ?? int.MinValue;

            JToken timestampToken;
            msgObjects.TryGetValue("timestamp", out timestampToken);
            var timestamp = timestampToken?.ToObject<long>() ?? 0;
            var time = DatetimeHelper.GetLocalTime(timestamp);
            System.Console.WriteLine($"Receive GatewayAttribute:{Environment.NewLine}" +
                                     $"gatewayUuid: {gatewayUuid}, gatewayType: {gatewayType}, " +
                                     $"dataType: {dataType}, time: {time}.");

            JToken dataToken;
            msgObjects.TryGetValue("data", out dataToken);
            var data = (JObject) dataToken;
            if (data == null)
            {
                System.Console.WriteLine("data is null");
                return;
            }

            switch (dataType)
            {
                case 0: //网关通用属性
                    var commonInfo = GetGatewayCommonAttribute(data);
                    System.Console.WriteLine(commonInfo);
                    break;
                case 1: //网关私有属性
                    var privateInfo = GetGatewayPrivateAttribute(data);
                    System.Console.WriteLine(privateInfo);
                    break;
            }
        }

        //网关通用属性
        private static GatewayCommonAttributeInfo GetGatewayCommonAttribute(JObject subObjects)
        {
            JToken ipToken;
            subObjects.TryGetValue("ip", out ipToken);
            var ip = ipToken?.ToObject<string>();

            JToken portToken;
            subObjects.TryGetValue("TCPPort", out portToken);
            var port = portToken?.ToObject<int>() ?? 0;

            JToken netMaskToken;
            subObjects.TryGetValue("netMask", out netMaskToken);
            var netMask = netMaskToken?.ToObject<string>();

            JToken netGatewayToken;
            subObjects.TryGetValue("netGateway", out netGatewayToken);
            var netGateway = netGatewayToken?.ToObject<string>();

            JToken statIntervalSecToken;
            subObjects.TryGetValue("statIntervalSec", out statIntervalSecToken);
            var statIntervalSec = statIntervalSecToken?.ToObject<int>() ?? 0;

            return new GatewayCommonAttributeInfo
            {
                Ip = ip,
                TcpPort = port,
                NetMask = netMask,
                NetGateway = netGateway,
                StatIntervalSec = statIntervalSec
            };
        }

        //网关私有属性
        private static GatewayPrivateAttributeInfo GetGatewayPrivateAttribute(JObject subObjects)
        {
            JToken startFreqToken;
            subObjects.TryGetValue("startFreq", out startFreqToken);
            var startFreq = startFreqToken?.ToObject<int>() ?? 0;

            JToken antennaGainToken;
            subObjects.TryGetValue("antenna_gain", out antennaGainToken);
            var antennaGain = antennaGainToken?.ToObject<int>() ?? 0;

            JToken rfChNumToken;
            subObjects.TryGetValue("RF_ChNum", out rfChNumToken);
            var rfChNum = rfChNumToken?.ToObject<int>() ?? 0;

            JToken channelDataRateToken;
            subObjects.TryGetValue("channelDataRate", out channelDataRateToken);

            var sfList = new List<Dictionary<string, bool>>();

            var jArray = (JArray)channelDataRateToken;
            if (jArray != null)
            {
                foreach (var token in jArray)
                {
                    var jObject = token.ToObject<JObject>();

                    var sfDic = new Dictionary<string, bool>();
                    for (var i = 7; i < 12; i++)
                    {
                        var propertyName = $"SF{i}";
                        JToken sfToken;
                        jObject.TryGetValue(propertyName, out sfToken);
                        var sf = sfToken?.ToObject<bool>() ?? false;
                        sfDic.Add(propertyName, sf);
                    }

                    sfList.Add(sfDic);
                }
            }

            return new GatewayPrivateAttributeInfo
            {
                StartFreq = startFreq,
                AntennaGain = antennaGain,
                RfChNum = rfChNum,
                SfList = sfList
            };
        }

        #endregion

        #region 节点属性信息

        //节点属性信息
        private void OnNodeAttribute(JObject msgObjects)
        {
            JToken nodeUuidToken;
            msgObjects.TryGetValue("nodeUUID", out nodeUuidToken);
            var nodeUuid = nodeUuidToken?.ToObject<string>();

            JToken gatewayUuidToken;
            msgObjects.TryGetValue("gatewayUUID", out gatewayUuidToken);
            var gatewayUuid = gatewayUuidToken?.ToObject<string>();

            JToken nodeTypeToken;
            msgObjects.TryGetValue("nodeType", out nodeTypeToken);
            var nodeType = nodeTypeToken?.ToObject<int>() ?? 0;

            JToken dataTypeToken;
            msgObjects.TryGetValue("dataType", out dataTypeToken);
            var dataType = dataTypeToken?.ToObject<int>() ?? int.MinValue;

            JToken timestampToken;
            msgObjects.TryGetValue("timestamp", out timestampToken);
            var timestamp = timestampToken?.ToObject<long>() ?? 0;
            var time = DatetimeHelper.GetLocalTime(timestamp);

            System.Console.WriteLine($"Receive NodeAttribute:{Environment.NewLine}" +
                                     $"nodeUuid: {nodeUuid}, gatewayUuid: {gatewayUuid}, nodeType: {nodeType}, " +
                                     $"dataType: {dataType}, time: {time}.");

            JToken dataToken;
            msgObjects.TryGetValue("data", out dataToken);
            var data = (JObject)dataToken;
            if (data == null)
            {
                System.Console.WriteLine("data is null");
                return;
            }

            switch (dataType)
            {
                case 0: //节点通用属性
                    var commonInfo = GetNodeCommonAttribute(data);
                    System.Console.WriteLine(commonInfo);
                    break;
                case 1: //节点私有属性
                    var privateInfo = GetNodePrivateAttribute(nodeType, data);
                    System.Console.WriteLine(privateInfo);
                    break;
            }
        }

        //节点通用属性
        private static NodeCommonAttributeInfo GetNodeCommonAttribute(JObject subObjects)
        {
            JToken sleepTimeSecToken;
            subObjects.TryGetValue("sleepTimeSec", out sleepTimeSecToken);
            var sleepTimeSec = sleepTimeSecToken?.ToObject<int>() ?? 0;

            JToken sendStatusIntervalMinToken;
            subObjects.TryGetValue("sendStatusIntervalMin", out sendStatusIntervalMinToken);
            var sendStatusIntervalMin = sendStatusIntervalMinToken?.ToObject<int>() ?? 0;

            JToken powerSourceToken;
            subObjects.TryGetValue("powerSource", out powerSourceToken);
            var powerSource = powerSourceToken?.ToObject<int>() ?? int.MinValue;
            var powerSourceStr = powerSource == 0 ? "battery" : "externalPower";

            JToken workModeToken;
            subObjects.TryGetValue("workMode", out workModeToken);
            var workMode = workModeToken?.ToObject<int>() ?? int.MinValue;

            JToken syncEnableToken;
            subObjects.TryGetValue("syncEnable", out syncEnableToken);
            var syncEnable = syncEnableToken?.ToObject<bool>() ?? false;

            JToken powerVoltageToken;
            subObjects.TryGetValue("powerVoltage", out powerVoltageToken);
            var powerVoltage = powerVoltageToken?.ToObject<double>() ?? 0;

            JToken nodeTempToken;
            subObjects.TryGetValue("nodeTemp", out nodeTempToken);
            var nodeTemp = nodeTempToken?.ToObject<int>() ?? 0;

            JToken powerSavingToken;
            subObjects.TryGetValue("powerSaving", out powerSavingToken);
            var powerSaving = powerSavingToken?.ToObject<bool>() ?? false;

            return new NodeCommonAttributeInfo
            {
                SleepTimeSec = sleepTimeSec,
                SendStatusIntervalMin = sendStatusIntervalMin,
                PowerSource = powerSourceStr,
                WorkMode = workMode,
                SyncEnable = syncEnable,
                PowerVoltage = powerVoltage,
                NodeTemp = nodeTemp,
                PowerSaving = powerSaving
            };
        }

        //节点私有属性
        private static NodePrivateAttributeInfo GetNodePrivateAttribute(int nodeType, JObject subObjects)
        {
            if (nodeType == 0)
            {
                System.Console.WriteLine("Unkonwn device");
                return null;
            }

            //nodeType == 1 triAccNode

            JToken acquireDataTypeToken;
            subObjects.TryGetValue("acquireDataType", out acquireDataTypeToken);
            var acquireDataType = acquireDataTypeToken?.ToObject<int>() ?? 0;

            JToken dataLengthToken;
            subObjects.TryGetValue("dataLength", out dataLengthToken);
            var dataLength = dataLengthToken?.ToObject<int>() ?? 0;

            JToken sensorFreqToken;
            subObjects.TryGetValue("sensorFreq", out sensorFreqToken);
            var sensorFreq = sensorFreqToken?.ToObject<int>() ?? 0;

            JToken sensorRangeToken;
            subObjects.TryGetValue("sensorRange", out sensorRangeToken);
            var sensorRange = sensorRangeToken?.ToObject<int>() ?? int.MinValue;

            JToken trigValueToken;
            subObjects.TryGetValue("trigValue", out trigValueToken);
            var trigValue = trigValueToken?.ToObject<float>() ?? 0;

            JToken highPassCutoffFreqToken;
            subObjects.TryGetValue("highPassCutoffFreq", out highPassCutoffFreqToken);
            var highPassCutoffFreq = highPassCutoffFreqToken?.ToObject<int>() ?? int.MinValue;

            JToken xAcquireEnableToken;
            subObjects.TryGetValue("xAcquireEnable", out xAcquireEnableToken);
            var xAcquireEnable = xAcquireEnableToken?.ToObject<bool>() ?? false;

            JToken yAcquireEnableToken;
            subObjects.TryGetValue("yAcquireEnable", out yAcquireEnableToken);
            var yAcquireEnable = yAcquireEnableToken?.ToObject<bool>() ?? false;

            JToken zAcquireEnableToken;
            subObjects.TryGetValue("zAcquireEnable", out zAcquireEnableToken);
            var zAcquireEnable = zAcquireEnableToken?.ToObject<bool>() ?? false;

            JToken xTrigEnableToken;
            subObjects.TryGetValue("xTrigEnable", out xTrigEnableToken);
            var xTrigEnable = xTrigEnableToken?.ToObject<bool>() ?? false;

            JToken yTrigEnableToken;
            subObjects.TryGetValue("yTrigEnable", out yTrigEnableToken);
            var yTrigEnable = yTrigEnableToken?.ToObject<bool>() ?? false;

            JToken zTrigEnableToken;
            subObjects.TryGetValue("zTrigEnable", out zTrigEnableToken);
            var zTrigEnable = zTrigEnableToken?.ToObject<bool>() ?? false;

            return new NodePrivateAttributeInfo
            {
                AcquireDataType = acquireDataType,
                DataLength = dataLength,
                SensorFreq = sensorFreq,
                SensorRange = sensorRange,
                TrigValue = trigValue,
                HighPassCutoffFreq = highPassCutoffFreq,
                XAcquireEnable = xAcquireEnable,
                YAcquireEnable = yAcquireEnable,
                ZAcquireEnable = zAcquireEnable,
                XTrigEnable = xTrigEnable,
                YTrigEnable = yTrigEnable,
                ZTrigEnable = zTrigEnable
            };
        }

        #endregion

        //节点数据
        private void OnNodeData(JObject msgObjects)
        {
            System.Console.WriteLine("===================== OnNodeData ======================");
        }
    }
}
